package com.cus.tom.transfer.trans.io;

/*
 * DirectoryReader 是对系统自带的 DocumentFile 的封装
 * 基于系统接口，实现文件夹的流化
 */


import android.content.ContentResolver;
import android.util.Log;

import androidx.documentfile.provider.DocumentFile;

import com.cus.tom.transfer.bean.FileType;
import com.cus.tom.transfer.bean.TypeFile;
import com.cus.tom.transfer.db.SendDao;
import com.plata.base.aclr.utils.LogTool;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;

public class DirectoryReader extends Thread {
    private final ProgressReporter reporter;
    private final ContentResolver resolver;
    private final List<TypeFile> files;
    private final Channel out;
    private final BufferPool bufferPool;
    private boolean success = false;
    public static String LOG_TAG = "tagg";
    final SendDao dao = new SendDao();
    final String flag = UUID.randomUUID().toString();

    public DirectoryReader(ContentResolver resolver, List<TypeFile> files, Channel out,
                           ProgressReporter reporter, BufferPool bufferPool) {
        this.resolver = resolver;
        this.files = files;
        this.out = out;
        this.reporter = reporter;
        this.bufferPool = bufferPool;
    }

    public String getFlag() {
        return flag;
    }

    public boolean isSuccess() {
        return success;
    }

    private void sendDir(TypeFile typeFile, final DocumentFile dir, final String basePath) throws IOException, InterruptedException {
        if (true) {
            throw new RuntimeException("should not be here");
        }
        if (!dir.exists()) {
            return;
        }
        String pathStr = dir.getName();
        if (pathStr == null) {
            return;
        }
        if (pathStr.startsWith(".")) {
            return; // ignore hidden
        }
        if (basePath.length() > 0) {
            pathStr = basePath + "/" + pathStr;
        }
        LogTool.d(LOG_TAG, "Now at: " + pathStr);
        byte[] path = pathStr.getBytes(StandardCharsets.UTF_8);
        final ByteBuffer header = bufferPool.pop();
        header.order(ByteOrder.BIG_ENDIAN)
                .putInt(path.length)
                .putInt(FileType.NONE.getCode())
                .putLong(-1) // directory
                .put(path)
                .flip();
        out.write(header);
        for (DocumentFile f : dir.listFiles()) {
            if (f.isFile()) {
                sendFile(typeFile, f, pathStr);
            } else if (f.isDirectory()) {
                sendDir(typeFile, f, pathStr);
            }
        }
    }

    int sendNum = 0;

    private boolean sendFile(final TypeFile typeFile, final DocumentFile file, final String basePath) throws IOException, InterruptedException {
        if (!file.exists() || !file.canRead()) {
            return false;
        }
        String pathStr = file.getName();
        if (pathStr == null) {
            return false;
        }
        if (pathStr.startsWith(".")) {
            return false; // ignore hidden
        }
        if (basePath.length() > 0) {
            pathStr = basePath + "/" + pathStr;
        }
        final byte[] path = pathStr.getBytes(StandardCharsets.UTF_8);
        final long length = file.length();
        final ByteBuffer header = bufferPool.pop();
        header.order(ByteOrder.BIG_ENDIAN)
                .putInt(path.length)
                .putInt(typeFile.type.getCode())
                .putLong(length)
                .put(path)
                .flip();
        out.write(header);
        final String name = file.getName();
        Log.d(LOG_TAG, "sendFile: " + name + " length=" + length);
        try (final InputStream in = resolver.openInputStream(file.getUri())) {
            if (in == null) {
                throw new IOException("can't open input stream");
            }
            long pos = 0;
            ++sendNum;
            reporter.report(name, 0, 0, sendNum, files.size());
            if (length > 0) {
                while (true) {
                    ByteBuffer buf = bufferPool.pop();
                    while (buf.remaining() > 0) {
                        int read = in.read(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
                        if (read < 0) {
                            break;
                        }
                        buf.position(buf.position() + read);
                    }
                    buf.flip();
                    if (buf.limit() < 1) {
                        bufferPool.push(buf);
                        break;
                    }
                    pos += buf.limit();
                    out.write(buf);
                    reporter.report(name, pos, length, sendNum, files.size());
                }
            }
        }
        return true;
    }

    @Override
    public void run() {
        try {
            for (TypeFile file : files) {
                DocumentFile entry = DocumentFile.fromFile(new File(file.path));
                if (entry != null) {
                    if (entry.isDirectory()) {
                        sendDir(file, entry, "");
                    } else if (entry.isFile()) {
                        boolean bo = sendFile(file, entry, "");
                        if (bo) {
                            dao.insert(file, flag);
                        }
                    }
                }
            }
            reporter.report(null, 0, 0, sendNum, files.size());

            ByteBuffer buffer = bufferPool.pop();
            buffer.order(ByteOrder.BIG_ENDIAN)
                    .putInt(0)
                    .putInt(FileType.NONE.getCode())
                    .putLong(0)
                    .flip();
            out.write(buffer); // bye
            out.close();
            success = true;
            Log.d(LOG_TAG, "DirectoryReader finished normally");
        } catch (IOException e) {
            Log.e(LOG_TAG, "DirectoryReader", e);
        } catch (InterruptedException e) {
            Log.e(LOG_TAG, "DirectoryReader Interrupted", e);
        }
    }
}
